问题描述:将一个由N行数字组成的三角形,如下图实列,设计一个算法,计算处三角形的由顶值至底的
一条路径,使得该路径经过的数字总和是最大的(最大权重)
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
动态规划的思路:这倒题目,只要知道有递归这个神奇东西的同学,肯定会想要用DFS深度优先搜索去逐层遍历每个节点,直至到底端,再不断的回溯...选择....再回溯....,问题是,这种深度优先搜索的方法,耗时非常的大,性能不高,特别是当给出的数据很大的时候,根本就解不出来。。。因为有很多点重复的进行递归回溯,导致求解问题的时间非常糟糕。如果有一个数据结构,可以记录每个状态,那就可以节省重复的工作从而提升效率。
用动态规划的办法解决这道题是非常高效的,使用该方法的前提是要找到初始状态以及状态转义方程。运用递归求解的时候经常是从上往下依次递归,不妨我们倒过来思考一下,可不可以从下往上递推回去呢??这道题要求我们求一条权重最大的路径,我的想法是,首先定义一个状态数组dp[x][y]去记录每个节点的累计权重(即该节点的子节点中,权重较大的一条路径的权重之和),其中x代表第x行,y是第x行中的第y列。其次呢,我们要选择初始状态,既然从最后一层开始向上递推,且最后层的节点没有子节点,故这一层中的dp[x][y]就相当于自身的权重值。确定了初始状态后,我们就需要确定递推公式了!很容易的可以知道,树中的每个父节点有两个子节点,要确定最大权重路径,就要从两个子节点中比较他们的dp值,选择其中较大的并加上父节点自身的权重,得到的值即为父节点的dp值。。。依次类推,直到到达顶点,结束递推,而顶点的dp[0][0]即为最大权重路径的总值。故此递推公式为:dp[x][y]=(dp[x+1][y+1]+map[x][y]>dp[x+1][y]+map[x][y]?dp[x+1][y+1]+map[x][y]:dp[x+1][y]+map[x][y]) 。其中map[x][y]即为该节点的权重!
实现代码:
- public class TreeProblem {
- public static void main(String[] args) {
- int [][]map=
- {{7,0,0,0,0},
- {3,8,0,0,0},
- {8,1,0,0,0},
- {2,7,4,4,0},
- {4,5,2,6,5}
- };
- //总共有多少行
- int n=map.length;
- //保存每个节点的累计权重的状态数组
- int [][]dp=new int[n][n];
- //自底向上,一次递推处一条到顶点最大权重的路径,第一步要初始化最底层的dp状态
- for(int i=0;i<n;i++){
- /*map[n-1][i];其中n-1代表树中的最后一排,i则是第i个子节点
- 因为最后排没有它自己的子节点了,故此dp[n-1][i]即是map[n-1][i]
- */
- dp[n-1][i]=map[n-1][i];
- }
- //开始从下往上一次递推到顶点处,顶点的累积值dp[0][0]即为最大权重
- //x=n-2即为从树中的倒数第二行开始(因为倒数第一行的dp值全知道且没有子节点)
- for(int x=n-2;x>=0;x--){
- for(int y=0;y<=x;y++){
- //状态转义方程
- dp[x][y]=(dp[x+1][y+1]+map[x][y]>dp[x+1][y]+map[x][y]?
- dp[x+1][y+1]+map[x][y]:
- dp[x+1][y]+map[x][y]
- );
- }
- }
- System.out.println(dp[0][0]);
- }
- }